﻿using System;
using System.Collections.Generic;
using System.Linq;
using HK.Core.Common.Data;
using HK.Core.Common.Protocol;
using HK.Core.Common.Protocol.Data;
using HK.Core.Network.Download.Protocol.Data;
using HK.Core.Utils;
using UnityEngine;

namespace HK.Core.Network.Download.Data
{
    /// <summary>
    /// 下载信息
    /// </summary>
    [Serializable]
    public sealed class DownloadInfo : JsonData<DownloadInfo>, IDownloadInfo
    {

#region Create

        /// <summary>
        /// 创建下载信息
        /// </summary>
        /// <param name="iUrl">URL</param>
        /// <param name="iVersion">版本号</param>
        /// <param name="iDownloadPath">本地临时下载目录(即，本地下载的临时目录，下载完毕并完成校验后，会从该路径拷贝到保存路径。即:SavePath)</param>
        /// <param name="iSavePath">保存路径</param>
        /// <param name="iSize">文件大小(单位:Byte)</param>
        /// <param name="iCrcCheckCode">校验码 - Crc</param>
        /// <param name="iMd5CheckCode">校验码 - Md5</param>
        /// <returns>下载信息</returns>
        public static IDownloadInfo Create(string iUrl, string iVersion, string iDownloadPath, 
            string iSavePath, long iSize, long iCrcCheckCode, string iMd5CheckCode)
        {
            var info = new DownloadInfo();
            return info.Init(iUrl, iVersion, iDownloadPath, iSavePath, iSize, iCrcCheckCode, iMd5CheckCode) ? info : null;
        }

#endregion
        
        /// <summary>
        /// 保护构造函数
        /// </summary>
        private DownloadInfo() { }

        /// <summary>
        /// Id
        /// </summary>
        public int Id { get; set; } = 0;

        [SerializeField]
        private string url = null;
        /// <summary>
        /// 下载地址
        /// </summary>
        public string Url
        {
            get => url;
            private set => url = value;
        }

        /// <summary>
        /// 文件名
        /// </summary>
        public string FileName { get; private set; } = null;
        
        [SerializeField]
        private string version = null;
        /// <summary>
        /// 版本
        /// </summary>
        public string Version
        {
            get => version;
            private set => version = value;
        }
        
        [SerializeField]
        private string downloadPath = null;
        /// <summary>
        /// 下载保存路径
        /// <para>* 该路径为，下载临时保存路径。下载成功并通过文件校验后，将拷贝到真正的保存路径。即:SavePath</para>
        /// </summary>
        public string DownloadPath 
        {
            get => downloadPath;
            private set => downloadPath = value;
        }
        
        [SerializeField]
        private string savePath = null;
        /// <summary>
        /// 保存路径
        /// </summary>
        public string SavePath 
        {
            get => savePath;
            private set => savePath = value;
        }

        [SerializeField]
        private long size = 0L;

        /// <summary>
        /// 文件大小
        /// <para>* 单位:Byte</para>
        /// </summary>
        public long Size
        {
            get => size;
            set => size = value;
        }

        [SerializeField]
        private long crcCheckCode = 0L;
        /// <summary>
        /// 校验码 - Crc
        /// </summary>
        public long CrcCheckCode 
        {
            get => crcCheckCode;
            private set => crcCheckCode = value;
        }

        [SerializeField]
        private string md5CheckCode = null;
        /// <summary>
        /// 校验码 - Md5
        /// </summary>
        public string Md5CheckCode 
        {
            get => md5CheckCode;
            private set => md5CheckCode = value;
        }

        private int _downloadStatus = (int)DownloadStatus.None;

        /// <summary>
        /// 下载中标志位
        /// </summary>
        public bool Downloading
        {
            get => UtilsBytes.CheckBytes(_downloadStatus, (int)DownloadStatus.Downloading);
            set
            {
                _downloadStatus = UtilsBytes.SetBytes(_downloadStatus, (int)DownloadStatus.Downloading, value);
                if (value)
                {
                    _downloadStatus = UtilsBytes.SetBytes(_downloadStatus, (int)DownloadStatus.Downloaded, false);
                }
            }
        }
        
        /// <summary>
        /// 下载完成标志位
        /// </summary>
        public bool Downloaded 
        {
            get => UtilsBytes.CheckBytes(_downloadStatus, (int)DownloadStatus.Downloaded);
            set
            {
                _downloadStatus = UtilsBytes.SetBytes(_downloadStatus, (int)DownloadStatus.Downloaded, value);
                if (value)
                {
                    _downloadStatus = UtilsBytes.SetBytes(_downloadStatus, (int)DownloadStatus.Downloading, false);
                }
            }
        }
        
        /// <summary>
        /// 下载完成标志位
        /// <para>* 自身以及所包含补丁全部下载完成</para>
        /// </summary>
        public bool AllDownloaded 
        {
            get
            {
                if (!Valid || !Downloaded) return false;
                if (null == Patch) return true;

                var flg = true;
                var patchTmp = Patch;
                while (null != patchTmp)
                {
                    if (!patchTmp.Valid || !patchTmp.Downloaded)
                    {
                        flg = false;
                        break;
                    }
                    patchTmp = patchTmp.Patch;
                }
                return flg;
            }
        }
        
        /// <summary>
        /// 解压中
        /// </summary>
        public bool Decompressing  
        {
            get => UtilsBytes.CheckBytes(_downloadStatus, (int)DownloadStatus.Decompressing);
            set
            {
                _downloadStatus = UtilsBytes.SetBytes(_downloadStatus, (int)DownloadStatus.Decompressing, value);
                if (value)
                {
                    _downloadStatus = UtilsBytes.SetBytes(_downloadStatus, (int)DownloadStatus.Decompressed, false);
                }
            }
        }
        
        /// <summary>
        /// 已解压
        /// </summary>
        public bool Decompressed 
        {
            get => UtilsBytes.CheckBytes(_downloadStatus, (int)DownloadStatus.Decompressed);
            set
            {
                _downloadStatus = UtilsBytes.SetBytes(_downloadStatus, (int)DownloadStatus.Decompressed, value);
                if (value)
                {
                    _downloadStatus = UtilsBytes.SetBytes(_downloadStatus, (int)DownloadStatus.Decompressing, false);
                }
            }
        }

        /// <summary>
        /// 下载耗时
        /// <para>* 单位:毫秒</para>
        /// </summary>
        public long CostTime { get; set; } = 0L;

        /// <summary>
        /// 有效标志位
        /// </summary>
        public override bool Valid => !string.IsNullOrEmpty(url) && !string.IsNullOrEmpty(version) && !string.IsNullOrEmpty(downloadPath) &&
                                      !string.IsNullOrEmpty(savePath) && !string.IsNullOrEmpty(FileName) && 0 < size;
        
        
#region Patch

        /// <summary>
        /// 补丁
        /// </summary>
        public IDownloadInfo Patch { get; private set; } = null;

        /// <summary>
        /// 设定补丁
        /// </summary>
        /// <param name="iPatch">补丁</param>
        public void SetPatch(IDownloadInfo iPatch) => Patch = iPatch;

#endregion

        /// <summary>
        /// 总个数
        /// <para>* 自身以及包含补丁的个数</para>
        /// </summary>
        public int TotalCount
        {
            get
            {
                if (!Valid) return 0;
                if (null == Patch) return 1;

                var count = 1;
                var patchTmp = Patch;
                while (null != patchTmp)
                {
                    ++count;
                    patchTmp = patchTmp.Patch;
                }
                return count;
            }
        }
                
        /// <summary>
        /// 总大小
        /// <para>* 自身以及包含补丁的数据大小(单位:Byte)</para>
        /// </summary>
        public long TotalSize 
        {
            get
            {
                if (!Valid) return 0L;
                if (null == Patch) return Size;

                var sizeTmp = Size;
                var patchTmp = Patch;
                while (null != patchTmp)
                {
                    sizeTmp += patchTmp.Size;
                    patchTmp = patchTmp.Patch;
                }
                return sizeTmp;
            }
        }
        
        /// <summary>
        /// 已下载数量
        /// <para>* 自身以及所包含补丁数量总和</para>
        /// </summary>
        public int DownloadedCount 
        {
            get
            {
                if (!Valid || !Downloaded) return 0;
                if (null == Patch) return 1;

                var downloadedCount = 1;
                var patchTmp = Patch;
                while (null != patchTmp)
                {
                    if (!patchTmp.Valid || !patchTmp.Downloaded)
                    {
                        break;
                    }

                    downloadedCount += 1;
                    patchTmp = patchTmp.Patch;
                }
                return downloadedCount;
            }
        }
        
        /// <summary>
        /// 已下载的大小
        /// <para>* 自身以及包含补丁的数据大小(单位:Byte)</para>
        /// </summary>
        public long DownloadedSize 
        {
            get
            {
                if (!Valid || !Downloaded) return 0L;
                if (null == Patch) return size;

                var sizeTmp = size;
                var patchTmp = Patch;
                while (null != patchTmp)
                {
                    if(!patchTmp.Valid || !patchTmp.Downloaded) break;
                    
                    sizeTmp += patchTmp.Size;
                    patchTmp = patchTmp.Patch;
                }
                return sizeTmp;
            }
        }

        /// <summary>
        /// 创建下载信息
        /// </summary>
        /// <param name="iUrl">URL</param>
        /// <param name="iVersion">版本号</param>
        /// <param name="iDownloadPath">本地临时下载目录(即，本地下载的临时目录，下载完毕并完成校验后，会从该路径拷贝到保存路径。即:SavePath)</param>
        /// <param name="iSavePath">保存路径</param>
        /// <param name="iSize">文件大小(单位:Byte)</param>
        /// <param name="iCrcCheckCode">校验码 - Crc</param>
        /// <param name="iMd5CheckCode">校验码 - Md5</param>
        /// <returns>下载信息</returns>
        public bool Init(string iUrl, string iVersion, string iDownloadPath,
            string iSavePath, long iSize, long iCrcCheckCode, string iMd5CheckCode)
        {
            if (string.IsNullOrEmpty(iUrl) || string.IsNullOrEmpty(iDownloadPath) || 
                string.IsNullOrEmpty(iSavePath) || 0L >= iSize) return false;

            if (0L >= iCrcCheckCode && string.IsNullOrEmpty(iMd5CheckCode)) return false;

            Url = iUrl;
            Version = iVersion;
            DownloadPath = iDownloadPath;
            SavePath = iSavePath;
            Size = iSize;

            // 取得文件名
            FileName = UtilsIo.GetFileNameByUrl(iUrl);

            CrcCheckCode = iCrcCheckCode;
            Md5CheckCode = iMd5CheckCode;
            Downloaded = false;
            
            return true;
        }

        /// <summary>
        /// 应用数据
        /// </summary>
        /// <param name="iData">数据</param>
        /// <param name="iImport">导入标志位(true:从相应的*.json文件导入; false:反之;)</param>
        /// <returns>true:有变更; false:无变更;</returns>
        public override bool ApplyData(IJsonData iData, bool iImport = false)
        {
            if (null == iData || !iData.Valid) return false;

            var dirty = base.ApplyData(iData, iImport);
            if (dirty && !iImport)
            {
                Dirty = true;
            }

            if (iData is IDownloadInfo info)
            {
                url = UpdateValue(url, info.Url, iImport);
                downloadPath = UpdateValue(downloadPath, info.DownloadPath, iImport);
                savePath = UpdateValue(savePath, info.SavePath, iImport);
                FileName = UpdateValue(FileName, info.FileName, iImport);
                version = UpdateValue(version, info.Version, iImport);
                size = UpdateValue(size, info.Size, iImport);
                crcCheckCode = UpdateValue(crcCheckCode, info.CrcCheckCode, iImport);
                md5CheckCode = UpdateValue(md5CheckCode, info.Md5CheckCode, iImport);
            }

            return Dirty;
        }

        /// <summary>
        /// 清空
        /// </summary>
        public override void Clear()
        {
            base.Clear();
            url = null;
            FileName = null;
            version = null;
            downloadPath = null;
            savePath = null;
            size = 0L;

            crcCheckCode = 0L;
            md5CheckCode = null;
            CostTime = 0L;
        }
    }

    /// <summary>
    /// 下载速度计算单元
    /// </summary>
    [Serializable]
    public sealed class DownloadSpeedUnit : JsonData<DownloadSpeedUnit>, IDownloadSpeedUnit
    {

#region Create

        /// <summary>
        /// 创建下载速度计算单元
        /// </summary>
        /// <param name="iKey">Point Key</param>
        /// <param name="iSize">数据大小(单位:Byte)</param>
        /// <param name="iTimeTicks">时间Ticks(单位:毫秒)</param>
        /// <returns>下载速度计算单元</returns>
        public static DownloadSpeedUnit Create(string iKey, long iSize, long iTimeTicks)
        {
            return new DownloadSpeedUnit { key = iKey, size = iSize, timeTicks = iTimeTicks };
        }

#endregion

        /// <summary>
        /// 保护构造函数
        /// </summary>
        private DownloadSpeedUnit() {}

        private string key = null;
        /// <summary>
        /// Point Key
        /// </summary>
        public string Key 
        {
            get => key;
            private set => key = value;
        }
        
        [SerializeField]
        private long size = 0L;
        /// <summary>
        /// 已下载的大小
        /// <para>* 单位:Byte</para>
        /// </summary>
        public long Size 
        {
            get => size;
            private set => size = value;
        }
        
        
        [SerializeField]
        private long timeTicks = 0L;
        /// <summary>
        /// 时间
        /// <para>单位 : 毫秒数</para>
        /// </summary>
        public long TimeTick 
        {
            get => timeTicks;
            private set => timeTicks = value;
        }

        /// <summary>
        /// 有效标志位
        /// </summary>
        public override bool Valid => !string.IsNullOrEmpty(key);

        /// <summary>
        /// 应用数据
        /// </summary>
        /// <param name="iData">数据</param>
        /// <param name="iImport">导入标志位(true:从相应的*.json文件导入; false:反之;)</param>
        /// <returns>true:有变更; false:无变更;</returns>
        public override bool ApplyData(IJsonData iData, bool iImport = false)
        {
            if (null == iData || !iData.Valid) return false;

            var dirty = base.ApplyData(iData, iImport);
            if (dirty && !iImport)
            {
                Dirty = true;
            }

            if (iData is IDownloadSpeedUnit unit)
            {
                key = UpdateValue(key, unit.Key, iImport);
                size = UpdateValue(size, unit.Size, iImport);
                timeTicks = UpdateValue(timeTicks, unit.TimeTick, iImport);
            }

            return Dirty;
        }

        /// <summary>
        /// 清空
        /// </summary>
        public override void Clear()
        {
            base.Clear();

            key = null;
            size = 0L;
            timeTicks = 0L;
        }
    }

    /// <summary>
    /// 下载速度
    /// </summary>
    [Serializable]
    public sealed class DownloadSpeed : JsonData<DownloadSpeed>, IDownloadSpeed<DownloadSpeedUnit>
    {

#region Create

            /// <summary>
            /// 创建下载速度
            /// </summary>
            /// <param name="iStartTimeTicks">开始时间Ticks(单位 : 毫秒数)</param>
            /// <param name="iSampleCount">采样数</param>
            /// <returns>下载速度</returns>
            public static DownloadSpeed Create(long iStartTimeTicks, int iSampleCount)
            {
                var speed = new DownloadSpeed();
                speed.Init(iStartTimeTicks, iSampleCount);
                return speed;
            }

#endregion
        
        /// <summary>
        /// 保护构造函数
        /// </summary>
        private DownloadSpeed() {}

        [SerializeField]
        private int sampleCount = 0;
        /// <summary>
        /// 动态采样数
        /// </summary>
        public int SampleCount
        {
            get => sampleCount;
            private set
            {
                sampleCount = Mathf.FloorToInt(value * 1.5f);
                _startSampleCount = Mathf.FloorToInt(value * 1.2f);
            }
        }

        private int _startSampleCount = 0;
        
        [SerializeField]
        private List<DownloadSpeedUnit> speedPoints = new List<DownloadSpeedUnit>();
        /// <summary>
        /// 速度点
        /// </summary>
        public List<DownloadSpeedUnit> SpeedPoints => speedPoints;

        private void Init(long iStartTimeTicks, int iSampleCount)
        {
            SampleCount = iSampleCount;
            // 追加一个假的其实节点
            var startPoint = DownloadSpeedUnit.Create("StartPoint", 0L, iStartTimeTicks);
            speedPoints.Add(startPoint);
        }

        private static System.Object _safeLock = new object();

        /// <summary>
        /// 总耗时
        /// <para>* 单位:毫秒</para>
        /// </summary>
        public long TotalCostTime
        {
            get
            {
                if (1 >= speedPoints.Count) return 0L;

                var min = 0L;
                var max = 0L;
                lock (_safeLock)
                {
                    min = speedPoints.Min(iO => iO.TimeTick);
                    max = speedPoints.Max(iO => iO.TimeTick);
                }

                var costTime = max - min;
                return 0L >= costTime ? 0L : costTime;
            }
        }

        /// <summary>
        /// 追加速度单位节点
        /// </summary>
        /// <param name="iPoint">速度节点</param>
        public void AddSpeedPoint(DownloadSpeedUnit iPoint)
        {
            if(null == iPoint || !iPoint.Valid) return;
            lock (_safeLock)
            {
                if (0 >= speedPoints.Count)
                {
                    speedPoints.Add(iPoint);
                    return;
                }

                if (!speedPoints.Exists(iO => iO.Key.Equals(iPoint.Key)))
                {
                    speedPoints.Add(iPoint);
                }
            }
        }

        /// <summary>
        /// 计算下载速度
        /// <para>* 单位:Byte/ms</para>
        /// </summary>
        /// <returns>下载速度</returns>
        public float CalcDownloadSpeed()
        {
            if (_startSampleCount > speedPoints.Count) return 0.0f;

            var count = speedPoints.Count;
            // 先全局算出平均量
            var totalSize = 0L;
            var startTimeTicks = 0L;
            var curTimeTicks = 0L;
            for (var idx = 0; idx < count; ++idx)
            {
                var point = speedPoints[idx];
                if (null == point || !point.Valid) continue;

                if (0 == idx)
                {
                    startTimeTicks = point.TimeTick;
                }
                curTimeTicks = curTimeTicks >= point.TimeTick ? curTimeTicks : point.TimeTick;
                totalSize += point.Size;
            }
            var totalDeltaTimeTicks = curTimeTicks - startTimeTicks;
            totalDeltaTimeTicks = 0L >= totalDeltaTimeTicks ? 0L : totalDeltaTimeTicks;
            var totalAverageSpeed = 0L >= totalDeltaTimeTicks ? 0.0f : (((float)totalSize) / totalDeltaTimeTicks);
            
            var deltaSpeeds = new List<float>();
            var sampleCnt = sampleCount >= speedPoints.Count ? speedPoints.Count : sampleCount;
            var startIndex = speedPoints.Count - sampleCnt;
            for (var idx = startIndex; idx < count - 1; ++idx)
            {
                var prePoint = speedPoints[idx];
                if(null == prePoint || !prePoint.Valid) continue;
                var nextPoint = speedPoints[idx + 1];
                if(null == nextPoint || !nextPoint.Valid) continue;
                
                var deltaSize = nextPoint.Size - prePoint.Size;
                var deltaTime = nextPoint.TimeTick - prePoint.TimeTick;
                var deltaSpeed = 0L == deltaTime ? 0.0f : ((float)deltaSize) / deltaTime;
                deltaSpeeds.Add(deltaSpeed);
            }

            var totalSpeed = deltaSpeeds.Sum();
            totalSpeed /= deltaSpeeds.Count;
            totalSpeed += totalAverageSpeed;
            totalSpeed = 0.0f >= totalSpeed ? totalAverageSpeed : totalSpeed;
            
            return totalSpeed;
        }

        /// <summary>
        /// 清空
        /// </summary>
        public override void Clear()
        {
            base.Clear();

            speedPoints.Clear();
        }
    }
}
